/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.fedoraifz.restrequests;

import java.io.IOException;
import java.net.MalformedURLException;

import javax.xml.bind.JAXBException;

import org.apache.log4j.Logger;
import org.igfay.jfig.JFig;
import org.igfay.jfig.JFigException;

import uk.ljmu.fedoraifz.utilities.GeneralUtilities;
import uk.ljmu.fedoraifz.wadl.fedora34.Objects;
import uk.ljmu.fedoraifz.wadl.fedora34.Pid;
import uk.ljmu.fedoraifz.wadl.fedora34.PidDatastreams;
import uk.ljmu.fedoraifz.wadl.fedora34.PidRelationships;
import uk.ljmu.fedoraifz.wadl.fedora34.Objects.NextPID;
import uk.ljmu.fedoraifz.wadl.fedora34.Pid.Validate;
import uk.ljmu.fedoraifz.wadl.fedora34.PidDatastreams.DsID;
import uk.ljmu.fedoraifz.wadl.fedora34.PidRelationships.New;

public class FedoraOperationsAPIMImpl implements FedoraOperationsAPIM {
    private static Logger log = Logger.getLogger(FedoraOperationsAPIMImpl.class);

    private UserCredentials userCreds;

    public void setUserCreds(UserCredentials userCreds) {
	this.userCreds = userCreds;
    }

    public UserCredentials getUserCreds() {
	return userCreds;
    }

    /**
     * Credentials come from configuration file
     * @throws JFigException 
     * @throws JFigException 
     */
    public FedoraOperationsAPIMImpl() throws JFigException {
	String user = JFig.getInstance().getValue("fedoraServer", "user").trim();
	String password = JFig.getInstance().getValue("fedoraServer", "password").trim();
	this.userCreds = new UserCredentials(user, password);
    }
    /**
     * Credentials come from application higher level
     * @param user
     * @param passwd
     */
    public FedoraOperationsAPIMImpl(String user, String passwd) {
	this.userCreds = new UserCredentials(user.trim(), passwd.trim());
    }
    
    /**
     * 
     * @return
     */
    public String getNextPID() {
	NextPID nextPid;
	try {
	    nextPid = new Objects.NextPID();
	    nextPid.post(userCreds);
	    return GeneralUtilities.convertStreamToString(nextPid.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
    /**
     * 
     * @param numpids
     * @param ns
     * @param format
     * @return
     */
    public String getNextPID(int numpids, String ns, String format) {
	NextPID nextPid;
	try {
	    nextPid = new Objects.NextPID();
	    nextPid.post(userCreds, numpids, ns, format);
	    return GeneralUtilities.convertStreamToString(nextPid.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
    /**
     * Creates an empty default datastream
     * @param pid
     * @param dsName
     * @return
     */
    public String addDatastream(String pid, String dsName, String contents, String mimetype) {
	DsID dsId;
	try {
	    dsId = new DsID(pid, dsName);
	    dsId.post(this.userCreds, contents, mimetype);
	    return GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    /**
     * Creates an new datastream
     * @param pid
     * @param dsName
     * @return
     */
    public String addDatastream(String pid, 
	    String dsName, 
	    String controlgroup, 
	    String dslocation, 
	    String altids, 
	    String dslabel, 
	    String versionable, 
	    String dsstate, 
	    String formaturi, 
	    String checksumtype, 
	    String checksum, 
	    String mimetype, 
	    String logmessage) {
	DsID dsId;
	try {
	    dsId = new DsID(pid, dsName);
	    dsId.post(userCreds, 
		    controlgroup, 
		    dslocation, 
		    altids, 
		    dslabel, 
		    Boolean.valueOf(versionable), 
		    dsstate, 
		    formaturi, 
		    checksumtype, 
		    checksum, 
		    mimetype, 
		    logmessage);

	    return GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
    public String createObject(String pid) {
	try {
	    Pid pidObj = new Pid(pid);
	    pidObj.post(this.userCreds, "text/xml");

	    return GeneralUtilities.convertStreamToString(pidObj.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    public String createObject(String pid,
	    String contents, 
	    String mimetype, 
	    String label,
	    String logmessage,
	    String format, 
	    String encoding, 
	    String namespace, 
	    String ownerid, 
	    String state, 
	    String ignoremime) {
	try {
	    Pid pidObj = new Pid(pid);
	    pidObj.post(userCreds, 
		    contents, 
		    mimetype, 
		    label, 
		    logmessage, 
		    format, 
		    encoding, 
		    namespace, 
		    ownerid, 
		    state, 
		    Boolean.valueOf(ignoremime));

	    return GeneralUtilities.convertStreamToString(pidObj.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    public String getDatastream(String pid, String dsname, String asofdatetime, String format, boolean validatechecksum) {

	DsID dsId;
	try {
	    dsId = new DsID(pid, dsname);
	    dsId.get(userCreds, asofdatetime, format, validatechecksum);

	    return GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    public String getDatastreams(String pid, String format,
	    String asOfDateTime) {
	try {
	    PidDatastreams pidDs = new PidDatastreams(pid);
	    pidDs.get();

	    return GeneralUtilities.convertStreamToString(pidDs.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	return null;
    }

    public String getDatastream(String pid, String dsname, String format,
	    String asOfDateTime, String validateChecksum) {
	DsID dsId;
	try {
	    dsId = new DsID(pid, dsname);
	    dsId.get(this.userCreds, asOfDateTime, format, Boolean.valueOf(validateChecksum));

	    return GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    public String deleteDatastream(String pidname, String dsname) {

	try {
	    DsID dsId = new DsID(pidname, dsname);
	    dsId.delete(this.userCreds);

	    return GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	// Errors
	return null;
    }

    public String deleteObject(String pidname) {
	Pid pid;
	try {
	    pid = new Pid(pidname);
	    pid.delete(this.userCreds);

	    return GeneralUtilities.convertStreamToString(pid.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}

	// Errors
	return null;
    }

    public String deleteDatastream(String pidname, String dsname,
	    String startdt, String enddt, String logmessage, String force) {

	try {
	    DsID dsId = new DsID(pidname, dsname);
	    dsId.delete(this.userCreds, startdt, enddt, logmessage);

	    return GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	// Errors
	return null;
    }

    public String getObjectXML(String pid) {

	try {
	    Pid.ObjectXML objXml = new Pid.ObjectXML(pid);

	    objXml.get(this.userCreds);

	    return GeneralUtilities.convertStreamToString(objXml.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    public String getDatastreamControlGroup(String pid, String dsname, String format,
	    String asOfDateTime, String validateChecksum) {
	String controlGroup = "";
	DsID dsId;
	try {
	    dsId = new DsID(pid, dsname);
	    dsId.get(this.userCreds, asOfDateTime, format, Boolean.valueOf(validateChecksum));
	    String returnData = GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();

	    controlGroup = GeneralUtilities.parseXmlElement(returnData, "dsControlGroup");
	    return controlGroup;
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    public String getDatastreamContent(String pid, 
	    String dsname) {
	DsID dsId;
	try {
	    dsId = new DsID(pid, dsname, true);
	    dsId.get(this.userCreds);

	    return GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    public String createObject() {
	try {
	    Pid pidObj = new Pid("new");
	    pidObj.post(this.userCreds, "text/xml");

	    return GeneralUtilities.convertStreamToString(pidObj.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
    public String addRelationship(String pid, String subject, String predicate,
	    String object, String isliteral, String datatype) {
	try {
	    if (pid == null || pid.equals(""))
		return null;
	    New newRel = new New(pid);
	    newRel.post(this.getUserCreds(), subject, predicate, object, Boolean.valueOf(isliteral), datatype);

	    return GeneralUtilities.convertStreamToString(newRel.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {			
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
    public String addRelationship(String pid) {
	try {
	    if (pid == null || pid.equals(""))
		return null;
	    New newRel = new New(pid);
	    newRel.post(getUserCreds());

	    return GeneralUtilities.convertStreamToString(newRel.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
    public String getRelationships(String pid, String subject,
	    String predicate, String object, String format) {
	try {
	    if (pid == null || pid.equals(""))
		return null;
	    PidRelationships pidRels = new PidRelationships(pid);
	    pidRels.get(this.getUserCreds(), subject, predicate, format);

	    return GeneralUtilities.convertStreamToString(pidRels.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	// Errors
	return null;
    }

    public String getRelationships(String pid) {
	try {
	    if (pid == null || pid.equals(""))
		return null;
	    PidRelationships pidRels = new PidRelationships(pid);
	    pidRels.get(this.getUserCreds());

	    return GeneralUtilities.convertStreamToString(pidRels.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	// Errors
	return null;
    }

    public String purgeRelationship(String pid) {
	try {
	    if (pid == null || pid.equals(""))
		return null;
	    PidRelationships pidRels = new PidRelationships(pid);
	    pidRels.delete(getUserCreds());

	    return GeneralUtilities.convertStreamToString(pidRels.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
    public String purgeRelationship(String pid, String subject,
	    String predicate, String object, String isliteral, String datatype) {

	try {
	    if (pid == null || pid.equals(""))
		return null;
	    
	    PidRelationships pidRels = new PidRelationships(pid);
	    pidRels.delete(getUserCreds(), subject, predicate, object, Boolean.valueOf(isliteral), datatype);

	    return GeneralUtilities.convertStreamToString(pidRels.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
    public String validate(String pid, String asofdatetime) {

	try {
	    if (pid == null || pid.equals(""))
		return null;
	    Validate val = new Validate(pid);

	    val.get(this.getUserCreds(), asofdatetime);

	    return GeneralUtilities.convertStreamToString(val.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	return null;
    }
    public void closeHttpConnections() {
    }
    
    public void setCredentials(String user, String password) {
	this.userCreds.setUser(user);
	this.userCreds.setPassword(password);
    }

    public String getFedoraUser() {
	return this.userCreds.getUser();
    }

    public String modifyObject(String pid, String label, String ownerId, String state,
	    String logMessage, String lastmodifieddate, String contents, String mimetype) {
	try {
	    Pid objXml = new Pid(pid);

	    objXml.put(this.userCreds, label, logMessage, ownerId, state, lastmodifieddate, contents, mimetype);

	    return GeneralUtilities.convertStreamToString(objXml.getRetVal().getInputStream()).trim();
	    
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }

    public String modifyDatastream(String pid, String dsid, String dslocation,
	    String altids, String dslabel, Boolean versionable, String dsstate,
	    String formaturi, String checksumtype, String checksum,
	    String mimetype, String logmessage, Boolean ignorecontent,
	    String lastmodifieddate) {
	DsID dsId;
	try {
	    dsId = new DsID(pid, dsid);
	    dsId.put(userCreds, dslocation, altids, dslabel, versionable, dsstate, formaturi, checksumtype, checksum, mimetype, logmessage, ignorecontent, lastmodifieddate);
	    
	    return GeneralUtilities.convertStreamToString(dsId.getRetVal().getInputStream()).trim();
	} catch (JAXBException e) {
	    log.error("Error", e);
	} catch (MalformedURLException e) {
	    log.error("Error", e);
	} catch (IOException e) {
	    log.error("Error", e);
	}
	//Errors
	return null;
    }
}
